
/************************************I2C Driver*******************************/
/**
 * Date : 2014.04.05
 */
/*
 * return +1       :busy
 * return  0       :AKC  / End Normal
 * return -2       :NACK
 * return -1       :Error
 * 在主发送模式，第九位AKC时钟是自动发送的。
 * 在主接收模式，第九位AKC时钟及数据位是软件发送的。
*/

#include"../Driver_PIC/system.h"
#include"../Driver_PIC/pic18_i2c.h"

void I2C1Init(unsigned char i2cMode)
{
    PMD1bits.MSSP1MD = 0;     //使能SSP1模块
    ANSELCbits.ANSC3 = 0;     //配置为数字接口
    ANSELCbits.ANSC4 = 0;     //配置为数字接口
    TRISCbits.RC3    = 1;     //接口要求
    TRISCbits.RC4    = 1;     //接口要求

    IPR1bits.SSP1IP  = 0;
    PIE1bits.SSP1IE  = 0;
    IPR2bits.BCL1IP  = 0;
    PIE2bits.BCL1IE  = 0;

    SSP1STATbits.SMP = 1;     //使能压摆控制
    SSP1STATbits.CKE = 0;     //禁止SMBus规范
    if(i2cMode == I2CMASTERMODE)
    {
        SSP1CON1bits.SSPM = 8;    //SSP配置为I2C主模式，时钟为FOSC/(4*(SSPADD+1))
        SSP1ADD           = SystemClock/4/100000-1; //设置波特率为300kbps
    }
    else
    {
        SSP1CON1bits.SSPM = 14;    //SSP配置为I2C从模式，7位地址，使能起始和停止位中断
        SSP1ADD           = 0x71 << 1; //设置从机地址
    }
    SSP1CON1bits.SSPEN = 1;       //使能I2C,将相关ＩＯ配置为SCL，SDA线
    if(i2cMode == I2CSLAVEMODE)
    {
        SSP1CON1bits.CKP   = 1;   //从模式使能时钟线，主模式无用
    }

    SSP1CON3bits.PCIE  = 1;   //允许停止条件中断，SSPM为6或7时有效
    SSP1CON3bits.SCIE  = 1;   //允许启动条件中断，SSPM为6或7时有效
    
    SSP1CON3bits.BOEN  = 0;   //只对从模式有效，仅在OV位为0时才更新BUF
    SSP1CON3bits.SDAHT = 1;   //SDA变化时间至少300ns
    
    SSP1CON3bits.SBCDE = 1;   //总线冲突中断
    
    SSP1CON3bits.AHEN  = 1;   //从器件接收到匹配地址时清零CKP，将时钟线拉低，需软件置位以使能SCL
    SSP1CON3bits.DHEN  = 1;   //从器件接收到数据时清零CKP，将时钟线拉低，需软件置位以使能SCL
}
/**************************************************************************/

__inline unsigned char I2C1DataRdy( void )
{
  if ( SSP1STATbits.BF )          // test if buffer full bit is set
    return ( +1 );                // data in SSP1BUF register ,the data is sending
  else
    return ( 0 );                 // no data in SSP1BUF register
}

/**************************************************************************/
/**
 * 读取一个字符
 * 执行后，应发送一个停止位
 * @return
 */
__inline unsigned char I2C1MasterRead( void )    //master mode only
{
  SSP1CON2bits.RCEN = 1;           // enable master for 1 byte reception
  while ( !SSP1STATbits.BF );      // wait until byte received
  return ( SSP1BUF );              // return with read byte
}
/******************************************************************************/
/**
 * 读取一个字符串
 * 返回读取最后一个字符后ACK的状态
 * 执行后，应发送一个停止位
 * @param rdptr
 * @param length
 * @return
 */
signed char I2C1MasterReadBytes( unsigned char *rdptr, unsigned char length )  //master mode only
{
    while ( length-- )                   // perform getcI2C1() for 'length' number of bytes
    {
        SSP1CON2bits.RCEN = 1;           // enable master for 1 byte reception
        while ( !SSP1STATbits.BF );      // wait until byte received
        *(rdptr++) = SSP1BUF;            // save byte received
        while ( SSP1CON2bits.RCEN );     // check that receive sequence is over
        if ( PIR2bits.BCL1IF )           // test for bus collision，SSP总线冲突
        {
            return ( -1 );               // return with Bus Collision error
        }
        if ( length )                    // test if 'length' bytes have been read
        {
            SSP1CON2bits.ACKDT = 0;      // set acknowledge bit state for ACK
            SSP1CON2bits.ACKEN = 1;      // initiate bus acknowledge sequence
            while ( SSP1CON2bits.ACKEN );   // wait until ACK sequence is over
        }
    }
    return ( 0 );                           // last byte received so don't send ACK
}

/***************************************************************************/
/**
 * 写一个字符或地址
 * 返回从器件的ACK状态
 * 执行后，如果返回ACK（0），可以继续调用发送函数，否则应发送一个停止位
 * @param data_out
 * @return
 */
signed char I2C1MasterWrite( unsigned char data_out ) //master mode only
{
    while(SSP1CON2bits.SEN | SSP1CON2bits.RSEN );
    SSP1BUF = data_out;             // write single byte to SSP1BUF
    if ( SSP1CON1bits.WCOL )        // test if write collision occurred
        return ( -1 );              // if WCOL bit is set return negative #
    else
    {
        while( SSP1STATbits.BF );   // wait until write cycle is complete
        I2C1Idle;                   // ensure module is idle
        if ( SSP1CON2bits.ACKSTAT ) // test for ACK condition received
   	    return ( -2 );          //Return NACK
        else return ( 0 );          //Return ACK
    }
}
/******************************************************************************/
/**
 * 写一个字符串到从器件
 * 返回从器件ACK状态
 * 执行后，如果返回ACK（0），可以继续调用发送函数，否则应发送一个停止位
 * @param wrptr
 * @param Length
 * @return
 */
signed char I2C1MasterWriteBytes( unsigned char *wrptr , unsigned char Length)
{
     while(SSP1CON2bits.SEN | SSP1CON2bits.RSEN );
     while ( Length -- )                                              // transmit data until null character
     {
         SSP1BUF = *wrptr++;               // write single byte to SSP1BUF
         if ( SSP1CON1bits.WCOL )        // test if write collision occurred
              return ( -1 );             // if WCOL bit is set return negative
         else
         {
	     while( SSP1STATbits.BF );   // wait until write cycle is complete
	     I2C1Idle;                 // ensure module is idle
	     if ( SSP1CON2bits.ACKSTAT ) // test for ACK condition received
	          return ( -2 );         //Return NACK
          }
    }                                  // continue data writes until null character
    if ( SSP1CON2bits.ACKSTAT ) // test for ACK condition received
	return ( -2 );         //Return NACK
    else return ( 0 );          //Return ACK
}

/******************************************************************************/
/**
 * 从器件回应主器件的数据请求，发送一个数据
 * 如果主器件要求继续发送，CKP将会拉低以延长SCL
 * 如果主器件不要求继续发送，CKP不被拉低
 * @param data_out
 * @return
 */
signed char I2C1SlaveWrite( unsigned char data_out ) //Slave mode only
{
    SSP1BUF = data_out;             // write single byte to SSP1BUF
    if ( SSP1CON1bits.WCOL )        // test if write collision occurred
        return ( -1 );              // if WCOL bit is set return negative #
    else
    {
        SSP1CON1bits.CKP = 1;        // release clock line
        while ( !PIR1bits.SSP1IF );  // wait until ninth clock pulse received
        if ( ( SSP1CON1bits.CKP ) && ( !SSP1STATbits.BF ) )// if R/W=0 and BF=0, NOT ACK was received
           {
                 return ( -2 );                     //Return NACK
            }
        else return(0);				//Return ACK
    }
}
/******************************************************************************/
signed char I2C1SlaveWriteBytes( unsigned char *wrptr , unsigned char Length)     // Slave transmitter
{
   while ( Length -- )
  {
      PIR1bits.SSP1IF = 0;           // reset SSP1IF bit
      SSP1BUF = *wrptr++;            // load SSP1BUF with new data
      SSP1CON1bits.CKP = 1;          // release clock line ,should to enable AHEN
      while ( !PIR1bits.SSP1IF );    // wait until ninth clock pulse received
      if ( ( SSP1CON1bits.CKP ) && ( !SSP1STATbits.BF ) )  // if R/W=0 and BF=0, NOT ACK was received
      {
          return ( -2 );             // terminate PutsI2C1() function
      }
  }                                  // continue data writes until null character
  return ( 0 );
}
